package com.xinke.query.common.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xinke.query.common.result.ResultCodeEnum;
import com.xinke.query.model.enums.QueryStatus;
import com.xinke.query.model.enums.QueryType;
import com.xinke.query.model.file.ResultFile;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Slf4j
public class GaoSuInfoUtils {
    // 日志记录器
    private static final Logger logger = LoggerFactory.getLogger(GaoSuInfoUtils.class);

    // 静态ObjectMapper实例，全局复用
    private static final ObjectMapper objectMapper = new ObjectMapper();

    //转map为Json
    public static String mapToString(HashMap<?, ?> map) {
        // 处理null值
        if (map == null) {
            return "null";
        }
        try {
            return objectMapper.writeValueAsString(map);
        } catch (JsonProcessingException e) {
            // 使用日志记录异常，而非直接打印堆栈
            logger.error("Failed to convert map to string", e);
            // 可以根据需求返回特定值或抛出运行时异常
            return "";
        }
    }


    /**
     * 长列别分段压缩保存
     *
     * @param longList
     * @param itemPerFile 每个文件包含的元素数量
     * @param zipFilePath 最终ZIP文件的保存路径
     * @param tempDir     临时JSON文件的存放目录
     * @param applyId     压缩包名
     * @param queryType   查询类型
     * @param <T>
     * @throws IOException
     */
    public static <T> void splitAndZip(List<T> longList, int itemPerFile, String zipFilePath, String tempDir, String applyId, QueryType queryType) throws IOException {
        // 创建临时目录
        File tempDirectory = new File(tempDir);
        if (!tempDirectory.exists()) {
            tempDirectory.mkdirs();
        }
        try {
            List<File> jsonFiles = splitListToJsonFiles(longList, itemPerFile, tempDir, applyId, queryType);
            zipFiles(jsonFiles, zipFilePath, applyId + "_" + queryType + ".zip");
            logger.info("成功生成ZIP文件" + zipFilePath);
            logger.info("共生成{}个文件", jsonFiles.size());
        } finally {
            //清理临时文件
        }

    }

    /**
     * 文件分段存为json
     *
     * @param list
     * @param itemPerFile 每个文件包含的元素数量
     * @param tempDir     json文件临时目录
     * @param applyId     为念名
     * @param <T>
     * @return
     * @throws IOException
     */
    private static <T> List<File> splitListToJsonFiles(List<T> list, int itemPerFile, String tempDir, String applyId, QueryType queryType) throws IOException {
        List<File> jsonFiles = new ArrayList<>();
        int totalItems = list.size();
        int totalFiles = (int) Math.ceil((double) totalItems / itemPerFile);
        for (int i = 0; i < totalFiles; i++) {
            int start = i * itemPerFile;
            int end = Math.min(start + itemPerFile, totalItems);
            List<T> subList = list.subList(start, end);
            //创建ResultFile
            ResultFile resultFile = new ResultFile((long) totalItems, totalFiles, subList.size(), i + 1, applyId, queryType, subList);
            String tempFileName = applyId + "_" + queryType.getCode() + "_" + (i + 1) + ".json";
            File jsonFile = new File(tempDir, tempFileName);
            objectMapper.writeValue(jsonFile, resultFile);
            jsonFiles.add(jsonFile);
        }
        return jsonFiles;
    }

    private static void zipFiles(List<File> files, String zipFilePath, String zipFileName) throws IOException {
        try (FileOutputStream fos = new FileOutputStream(zipFilePath + "/" + zipFileName); ZipOutputStream zos = new ZipOutputStream(fos)) {
            for (File file : files) {
                try (FileInputStream fis = new FileInputStream(file)) {
                    ZipEntry zipEntry = new ZipEntry(file.getName());
                    zos.putNextEntry(zipEntry);
                    byte[] buffer = new byte[1024];
                    int length;
                    while ((length = fis.read(buffer)) != -1) {
                        zos.write(buffer, 0, length);
                    }
                    zos.closeEntry();
                }
            }
        }
    }

    /**
     * 清理临时文件
     */
    private static void deleteTempFiles(File directory) {
        if (directory.exists()) {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.delete()) {
                        System.out.println("已删除临时文件: " + file.getAbsolutePath());
                    }
                }
            }
            directory.delete();
        }
    }

    /**
     * 计算byte[]的md5
     *
     * @param fileContent
     * @return
     */
    public static String calculateMD5(byte[] fileContent) {
        if (fileContent == null || fileContent.length == 0) {
            return null;
        }
        MessageDigest md = null;
        try {
            md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(fileContent);
            StringBuilder sb = new StringBuilder();
            for (byte b : digest) {
                sb.append(String.format("%02x", b & 0xff));
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            // 理论上不会发生，因为MD5是Java标准算法
            System.err.println("MD5算法不支持：" + e.getMessage());
        }
        return null;
    }


    /**
     * 根据status类型返回对应的错误码
     *
     * @param queryStatus
     * @return
     */
    public static ResultCodeEnum getResultCodeEnum(QueryStatus queryStatus) {
        if (queryStatus == QueryStatus.INBOUND) {
            return ResultCodeEnum.DATA_NOT_READY_PLEASE_RETRY_LATER;
        } else if (queryStatus == QueryStatus.PROCESSING) {
            return ResultCodeEnum.DATA_NOT_READY_PLEASE_RETRY_LATER;
        } else if (queryStatus == QueryStatus.QUERYFAIL) {
            return ResultCodeEnum.APPLY_QUERY_STORAGE_FAILED;
        }
        return ResultCodeEnum.SUCCESS;
    }

}
